/**
 * @(#)CoordinadorBundlesSimple.java Jan 29, 2007
 *
 * Copyright (c) 2005-2010 Psiqueware S.A. de C.V. All Rights Reserved.
 * This software is the proprietary information Psiqueware S.A. de C.V.
 * Use is subject to license terms.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * <br/>Usuario : AngelElectrico
 * <br/>Creaci&oacute;n: Jan 29, 2007 - 12:57:49 PM
 * <br/>
 * <br/>
 * <br/><t5>Cambios :</t5>
 * <br/><b>Fecha | Autor  |  Cambios</b>
 * <ul>
 * <li>Feb 16, 2007 | <a href = "mailto:AngelElectrico@PsiqueWare.com">Angel El&eacute;ctrico</a> | Creaci&oacute;n del Objeto.</li>
 *
 * </ul>
 */

package com.psiqueware.alheli.i18n.propertyBundles;

import com.psiqueware.alheli.i18n.CoordinadorBundles;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

/**
 * Coordinador de Bundles que tiene contextos a los que se puede acceder mediante varios hilos a la vez.
 * m&eacute;todos especiales para ser utilizados por diferentes threads.
 * <br/>Paquete Original: com.psiqueware.basico.i18n.propertyBundles
 * <br/>Nombre  Original: CoordinadorBundlesSimple
 * <br/>Oprima para ver : <a href="CoordinadorBundlesSimple.java.html"><i>C&oacute;digo Fuente</i> </a>
 *
 * @author <a href="mailto:AngelElectrico@PsiqueWare.com">Angel El&eacute;ctrico</a>
 * @version Revision: 1.1 Feb 16, 2007 12:57:49 PM
 */
public class CoordinadorBundlesSincronizado implements CoordinadorBundles
{

    /**
     * Mapa con las definiciones de los bundles.
     */
    private Map<String, ElementoCache> m_cache = new HashMap<String, ElementoCache>();


    /**
     * Nombres de los Bundles.
     */
    private final List<String> m_contextosBundles = new ArrayList<String>();

    /**
     * Constructor de CoordinadorBundlesSincronizado.
     */
    public CoordinadorBundlesSincronizado()
    {

    }


    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada.
     *
     * @param contexto         en esta implementaci&oacute;n no se utiliza.
     * @param nombreBaseBundle nombre base del bundle.
     */
    public void agregarResourceBundle(String contexto, String nombreBaseBundle)
    {
        assert (null != nombreBaseBundle) : ("<error>El argumento 'nombreBaseBundle' no puede ser NULL.</error>");
        assert (null != contexto) : ("<error>El argumento 'contexto' no puede ser NULL.</error>");
        final Locale locale = new Locale("");
        final ResourceBundle bundle = ResourceBundle.getBundle(nombreBaseBundle, locale);
        assert (null != bundle) : ("<error>El Bundle '" + nombreBaseBundle + "', no existe.</error>");
        synchronized (m_contextosBundles)
        {
            if (!m_contextosBundles.contains(contexto))
            {
                m_contextosBundles.add(contexto);
                final ElementoCache cache = new ElementoCache();
                cache.contexto = contexto;
                cache.nombreBase = nombreBaseBundle;
                agregaAlCache(locale, bundle, cache);
                m_cache.put(contexto, cache);
            }
        }
    }

    private Map<String, String> agregaAlCache(final Locale locale, final ResourceBundle bundle, final ElementoCache cache)
    {
        final Enumeration<String> keys = bundle.getKeys();
        final Map<String, String> llaves = new HashMap<String, String>();
        while (keys.hasMoreElements())
        {
            final String s = keys.nextElement();
            llaves.put(s, bundle.getString(s));
        }
        cache.elementos.put(locale, llaves);
        return llaves;
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param contexto en el que se definir&aacute; el Bundle.
     * @param baseName nombre base del bundle.
     *
     * @return {@link ResourceBundle} buscado.
     */
    public ResourceBundle agregaBuscaResourceBundle(String contexto, String baseName, Locale locale)
    {
        agregarResourceBundle(contexto, baseName);
        return getBundle(locale, contexto);
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada.
     *
     * @param contexto      en el que se definir&aacute; el Bundle.
     * @param classBaseName nombre base del bundle.
     */
    public void agregarResourceBundle(String contexto, Class classBaseName)
    {
        agregarResourceBundle(contexto, classBaseName.getName());
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada.
     *
     * @param baseName nombre base del bundle.
     */
    public void agregarResourceBundle(final String baseName)
    {
        agregarResourceBundle("", baseName);
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada.
     *
     * @param classBaseName nombre base del bundle.
     */
    public void agregarResourceBundle(final Class classBaseName)
    {
        agregarResourceBundle("", classBaseName);
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param contexto      en el que se definir&aacute; el Bundle.
     * @param classBaseName nombre base del bundle.
     *
     * @return {@link ResourceBundle} buscado.
     */
    public ResourceBundle agregaBuscaResourceBundle(String contexto, Class classBaseName, Locale locale)
    {
        return agregaBuscaResourceBundle(contexto, classBaseName.getName(), locale);
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param classBaseName nombre base del bundle.
     * @param locale        {@link Locale} del bundle buscado.
     *
     * @return {@link ResourceBundle} buscado.
     */
    public ResourceBundle agregaBuscaResourceBundle(final Class classBaseName, final Locale locale)
    {
        return agregaBuscaResourceBundle("", classBaseName, locale);
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param baseName nombre base del bundle.
     *
     * @return {@link ResourceBundle} buscado.
     */
    public ResourceBundle agregaBuscaResourceBundle(final String baseName)
    {
        return agregaBuscaResourceBundle("", baseName, Locale.getDefault());
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param classBaseName nombre base del bundle.
     *
     * @return {@link ResourceBundle} buscado.
     */
    public ResourceBundle agregaBuscaResourceBundle(final Class classBaseName)
    {
        return agregaBuscaResourceBundle("", classBaseName, Locale.getDefault());
    }

    /**
     * Devuelve un ResourceBundle para un {@link Locale} sin tomar en cuenta el contexto.
     *
     * @param locale   localidad cuyos recursos se buscan.
     * @param contexto para el Bundle. No se utiliza pero cumple con el contrato de la interfaze.
     *
     * @return {@link ResourceBundle}.
     */
    public ResourceBundle getBundle(Locale locale, String contexto)
    {
        assert m_contextosBundles.contains(contexto) : "<error>No se encontr&oacute; el contexto en el Coordinador.</error>";
        final CoordinadorBundlesSincronizado.ElementoCache cache = m_cache.get(contexto);
        Map<String, String> bundle = cache.elementos.get(locale);
        if (null == bundle)
        {
            final ResourceBundle nuevoBundle = ResourceBundle.getBundle(cache.nombreBase, locale);
            bundle = agregaAlCache(locale, nuevoBundle, cache);
        }
        return new ResourceBundleMapa(bundle);

    }

    /**
     * Devuelve un ResourceBundle para el {@link Locale} sin importarle el contexto de los recursos.
     *
     * @param locale localidad cuyos recursos se buscan.
     *
     * @return {@link ResourceBundle}.
     */
    public ResourceBundle getBundle(Locale locale)
    {
        final BasicoResourceBundleCombinado combinado = new BasicoResourceBundleCombinado(locale);
        synchronized (m_contextosBundles)
        {
            for (String contexto : m_contextosBundles)
            {
                final ResourceBundle bundle = getBundle(locale, contexto);
                combinado.agregarRecursos(bundle);
            }
        }
        return combinado;
    }

    /**
     * Devuelve un ResourceBundle sin importarle el contexto de los recursos y utilizando el {@link Locale} por defecto.
     *
     * @return {@link ResourceBundle}.
     */
    public ResourceBundle getBundle()
    {
        return getBundle(Locale.getDefault(), "");
    }


    private class ElementoCache
    {

        public Map<Locale, Map<String, String>> elementos = new HashMap<Locale, Map<String, String>>();

        public String nombreBase;

        public String contexto;
    }
}